#ifndef MATERIAL_H
#define MATERIAL_H

#include"hittable.h"


class material
{
public:

	virtual ~material()=default;

	virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const { return false; }


};


class lambertian :public material {
public:
	lambertian(const color& _albedo): albedo(_albedo){}

	bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {
		auto scattered_dir = rec.norm + random_unit_vector();//Monte Carlo scatter direction

		//small vector
		if (scattered_dir.near_zero()) {
			scattered_dir = rec.norm;
		}

		scattered = ray(rec.pt, scattered_dir);
		attenuation = albedo;
		return true;
		 }

private:
	color albedo;
};

class metal :public material {
public:
	metal(const color& _albedo,double fuzz): albedo(_albedo),fuzz(fuzz<1?fuzz:1) {}
	bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {
		vec3 reflected = reflect(r_in.direction(), rec.norm);

		reflected = unit_vector(reflected) + (fuzz * random_unit_vector());

		scattered = ray(rec.pt, reflected);

		attenuation = albedo;
		return (dot(scattered.direction(),rec.norm)>0);

		}

private:
	color albedo;
	double fuzz;// non-specular effect

};

//refractable material
class dielectric :public material {
public:
	dielectric(double _refraction_index) : refraction_index(_refraction_index) {}

	bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {
		attenuation = color(1.0, 1.0, 1.0);

		double ri = rec.front_face ? (1.0 / refraction_index) : refraction_index;// refract direction

		vec3 unit_dir = unit_vector(r_in.direction());
		double cos_theta = std::fmin(dot(-unit_dir, rec.norm), 1.0);
		double sin_theta = std::sqrt(1 - cos_theta * cos_theta);

		bool cannot_refract = ri * sin_theta > 1.0;//refract limit
		vec3 direction;
		if (cannot_refract||reflectance(cos_theta,ri)>random_double()) {
			direction = reflect(unit_dir, rec.norm);
		}
		else {
			direction = refract(unit_dir, rec.norm, ri);
		}

		scattered = ray(rec.pt, direction);

		return true;
	}

private:

	double refraction_index;

	//reflect radiance
	static double reflectance(double cos, double n) {
		//Schlicks approximation

		auto r0 = (1 - n) / (1 + n);
		r0 = r0 * r0;
		return r0 + (1 - r0) * std::pow((1 - cos), 5);
	}

};








#endif // !MATERIAL_H
